package com.tca.common.cloud2standalone.component;

import com.tca.common.cloud2standalone.dto.HttpServletRequestStandalone;
import com.tca.common.core.bean.Result;
import com.tca.common.core.utils.BeanUtils;
import com.tca.common.core.utils.ValidateUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.List;

/**
 * @author zhoua
 * @date 2022/1/14 10:51
 */
public class FeignProxyHandler<T> implements InvocationHandler {
    
    private Class<T> clazz;
    
    private BeanFactory beanFactory;
    
    private String prefix;
    
    private static RequestMappingHandlerMapping handlerMapping;
    
    private static final String INNER_CLASS_NAME_PREFIX = "<";
    
    private static final String INNER_CLASS_NAME_SUFFIX = ">";
    
    public FeignProxyHandler(Class<T> clazz, BeanFactory beanFactory, String prefix) {
        this.clazz = clazz;
        this.beanFactory = beanFactory;
        this.prefix = prefix;
    }
    
    private RequestMappingHandlerMapping getHandlerMapping() {
        if (handlerMapping == null) {
            synchronized (FeignProxyHandler.class) {
                if (handlerMapping == null) {
                    // 直接用beanName == requestMappingHandlerMapping, 根据类型取
                    // 当第三方jar同时引用springmvc和springsecurity时, springsecurity也提供了一个 RequestMappingHandlerMapping,
                    // 这里我们需要使用springmvc内置的即可
                    handlerMapping = (RequestMappingHandlerMapping) beanFactory.getBean("requestMappingHandlerMapping");
                }
            }
        }
        return handlerMapping;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        RequestMappingHandlerMapping requestMappingHandlerMapping = getHandlerMapping();
    
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }
        // 获取RequestMapping相关属性, 使用 AnnotatedElementUtils#findMergedAnnotation 方法, 可以获取到GetMapping、PostMapping等
        RequestMapping methodMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
        String path = prefix + methodMapping.value()[0];
        RequestMethod[] requestMethods = methodMapping.method();
        String methodType = requestMethods.length == 0? "": requestMethods[0].name();
        // 组装HttpServletRequest, 通过HttpServletRequest获取对应的HandlerMethod!!!
        HandlerExecutionChain handler = requestMappingHandlerMapping.getHandler(new HttpServletRequestStandalone(path, methodType));
        HandlerMethod handlerMethod = (HandlerMethod) handler.getHandler();
        
        // TODO 入参出参转化优化
        Class<?>[] parameterTypes = handlerMethod.getMethod().getParameterTypes();
    
        // 判断参数是否为数值类型或字符串类型, 如果不是, 则进行类型转换
        if (args != null && args.length == 1
                && !Number.class.isAssignableFrom(args[0].getClass())
                && args[0].getClass() != String.class
                && args[0].getClass() != Boolean.class
                && args[0].getClass() != parameterTypes[0]) {
            Object transferParam = BeanUtils.deepCopyProperties(args[0], parameterTypes[0]);
            args[0] = transferParam;
        }
    
        // data为空, 直接返回
        Result result = (Result) handlerMethod.getMethod().invoke(handlerMethod.getBean(), args);
        if (ValidateUtils.isEmpty(result.getData()) || result.getData().getClass() == Boolean.class) {
            return result;
        }
        
        // 获取返回类型中的泛型类型
        Type actualTypeArgument = ((ParameterizedTypeImpl) method.getGenericReturnType()).getActualTypeArguments()[0];
        String outerClassName = actualTypeArgument.getTypeName();
        String innerClassName;
        Class outerClass = null;
        Class innerClass = null;
        if (outerClassName.contains(INNER_CLASS_NAME_PREFIX) && outerClassName.contains(INNER_CLASS_NAME_SUFFIX)) {
            int innerClassNameStart = outerClassName.indexOf(INNER_CLASS_NAME_PREFIX);
            int innerClassNameEnd = outerClassName.indexOf(INNER_CLASS_NAME_SUFFIX);
            outerClass = Class.forName(outerClassName.substring(0, innerClassNameStart));
            innerClass = Class.forName(outerClassName.substring(innerClassNameStart + 1, innerClassNameEnd));
        } else {
            outerClass = Class.forName(outerClassName);
        }
        
        
        Object data = result.getData();
        if (ValidateUtils.isNotEmpty(data) && !Number.class.isAssignableFrom(data.getClass())
            && data.getClass() != String.class) {
            
            if (ValidateUtils.isEmpty(innerClass)) {
                data = BeanUtils.deepCopyProperties(data, outerClass);
            } else {
                data = BeanUtils.deepCopyListProperties((List)data, innerClass);
            }
            
        }
        result.setData(data);
        return result;
    }
    
    
}
